using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Microsoft.Samples.SqlServer.Dts
{
    public partial class RemoveDuplicatesForm : Form
    {
        #region Exposed events

        internal event GetAvailableColumnsEventHandler GetAvailableColumns;
        internal event ChangeInputOutputColumnsEventHandler SetInputOutputColumns;
        internal event ChangeInputOutputColumnsEventHandler DeleteInputOutputColumns;
        internal event GetSelectedInputOutputColumnsEventHandler GetSelectedInputOutputColumns;
        internal event ChangeOutputColumnNameEventHandler ChangeOutputColumnName;

        #endregion

        // This flag is set to true while loading the state from the component, to disable
        // the grid events during that time.
        bool isLoading;

        public RemoveDuplicatesForm()
        {
            InitializeComponent();
        }

        #region Helper functions

        /// <summary>
        /// Hooking up a data flow element to a grid cell.
        /// </summary>
        /// <param name="cell"></param>
        /// <param name="dataFlowElement"></param>
        private static void SetGridCellData(DataGridViewCell cell, DataFlowElement dataFlowElement)
        {
            cell.Value = dataFlowElement.ToString();
            cell.Tag = dataFlowElement.Tag;
            cell.ToolTipText = dataFlowElement.ToolTip;
        }

        /// <summary>
        /// Setting selected columns (input and output ones) to appropriate cells in the bottom grid
        /// </summary>
        /// <param name="selectedColumns"></param>
        /// <param name="row"></param>
        private void SetSelectedColumnsRow(SelectedInputOutputColumns selectedColumns, DataGridViewRow row)
        {
            DataGridViewCellCollection cells = row.Cells;

            SetGridCellData(cells[this.gridColumnInputColumn.Index], selectedColumns.InputColumn);
            SetGridCellData(cells[this.gridcolumnDistinctOutputColumn.Index], selectedColumns.DistinctColumn);
            SetGridCellData(cells[this.gridcolumnDuplicateOutputColumn.Index], selectedColumns.DuplicateColumn);
        }

        /// <summary>
        /// Loading available columns to the upper grid.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")]
        private void LoadAvailableColumns()
        {
            if (this.GetAvailableColumns != null)
            {
                AvailableColumnsArgs args = new AvailableColumnsArgs();
                IAsyncResult res = this.GetAvailableColumns.BeginInvoke(this, args, null, null);
                this.GetAvailableColumns.EndInvoke(res);

                if (args.AvailableColumns != null && args.AvailableColumns.Length > 0)
                {
                    this.dataGridViewAvailableColumns.Rows.Add(args.AvailableColumns.Length);
                    for (int i = 0; i < args.AvailableColumns.Length; i++)
                    {
                        AvailableColumnElement availableColumnRow = args.AvailableColumns[i];

                        // Filling the cells.
                        this.dataGridViewAvailableColumns.Rows[i].Cells[this.gridColumnCheckbox.Index].Value = availableColumnRow.Selected;
                        SetGridCellData(this.dataGridViewAvailableColumns.Rows[i].Cells[this.gridColumnAvailableColumns.Index], availableColumnRow.AvailableColumn);
                    }
                }
            }
        }

        /// <summary>
        /// Loading already selected columns to populate the bottom grid.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1817:DoNotCallPropertiesThatCloneValuesInLoops")]
        private void LoadSelectedColumns()
        {
            if (this.GetSelectedInputOutputColumns != null)
            {
                SelectedInputOutputColumnsArgs args = new SelectedInputOutputColumnsArgs();
                IAsyncResult res = this.GetSelectedInputOutputColumns.BeginInvoke(this, args, null, null);
                this.GetSelectedInputOutputColumns.EndInvoke(res);

                if (args.SelectedColumns != null && args.SelectedColumns.Length > 0)
                {
                    this.dataGridViewSelectedColumns.Rows.Add(args.SelectedColumns.Length);
                    for (int i = 0; i < args.SelectedColumns.Length; i++)
                    {
                        this.SetSelectedColumnsRow(args.SelectedColumns[i], this.dataGridViewSelectedColumns.Rows[i]);
                    }
                }
            }
        }

        /// <summary>
        /// Adding newly selected columns to the Data Flow and the bottom grid.
        /// </summary>
        /// <param name="args"></param>
        private void SetColumns(SetInputOutputColumnsArgs args)
        {
            if (this.SetInputOutputColumns != null)
            {
                IAsyncResult res = this.SetInputOutputColumns.BeginInvoke(this, args, null, null);
                this.SetInputOutputColumns.EndInvoke(res);

                // If call to the OM succeeded, update the bottom grid.
                if (!args.CancelAction)
                {
                    // Add the new row.
                    this.dataGridViewSelectedColumns.Rows.Add();
                    // And fill its cells.
                    this.SetSelectedColumnsRow(args.GeneratedColumns, this.dataGridViewSelectedColumns.Rows[this.dataGridViewSelectedColumns.RowCount - 1]);
                }
            }
        }

        /// <summary>
        /// Deleting unselected columns from the Data Flow and the bottom grid.
        /// </summary>
        /// <param name="args"></param>
        private void DeleteColumns(SetInputOutputColumnsArgs args)
        {
            if (this.DeleteInputOutputColumns != null)
            {
                IAsyncResult res = this.DeleteInputOutputColumns.BeginInvoke(this, args, null, null);
                this.DeleteInputOutputColumns.EndInvoke(res);

                // If call to the OM succeeded, update the bottom grid.
                if (!args.CancelAction)
                {
                    foreach (DataGridViewRow row in this.dataGridViewSelectedColumns.Rows)
                    {
                        // Look for a related row.
                        if (row.Cells[this.gridColumnInputColumn.Index].Value.ToString() == args.VirtualColumn.ToString())
                        {
                            // And remove it, if found.
                            this.dataGridViewSelectedColumns.Rows.Remove(row);
                            break;
                        }
                    }
                }
            }
        }

        #endregion

        #region Overridden methods

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            this.isLoading = true;
            try
            {
                // Loading available and previously selected columns.
                this.LoadAvailableColumns();
                this.LoadSelectedColumns();
            }
            finally
            {
                this.isLoading = false;
            }
        }

        #endregion

        #region Event handlers

        // Commiting change in the checbox columns so it will trigger CellValueChanged immediately.
        private void dataGridViewAvailableColumns_CurrentCellDirtyStateChanged(object sender, EventArgs e)
        {
            if (this.dataGridViewAvailableColumns.CurrentCell != null && this.dataGridViewAvailableColumns.CurrentCell is DataGridViewCheckBoxCell)
            {
                this.dataGridViewAvailableColumns.CommitEdit(DataGridViewDataErrorContexts.Commit);
            }
        }

        private void dataGridViewAvailableColumns_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            // Ignoring this event while loading columns.
            if (this.isLoading)
            {
                return;
            }

            if (e.ColumnIndex == this.gridColumnCheckbox.Index && e.RowIndex >= 0)
            {
                DataGridViewCheckBoxCell checkBoxCell = this.dataGridViewAvailableColumns.CurrentCell as DataGridViewCheckBoxCell;
                DataGridViewCell columnCell = this.dataGridViewAvailableColumns.Rows[e.RowIndex].Cells[this.gridColumnAvailableColumns.Index];

                SetInputOutputColumnsArgs args = new SetInputOutputColumnsArgs();
                args.VirtualColumn = new DataFlowElement(columnCell.Value.ToString(), columnCell.Tag);

                if ((bool)checkBoxCell.Value)
                {
                    // Set used columns if the checkbox is selected.
                    this.SetColumns(args);
                }
                else
                {
                    // Delete columns if the checkbox is unselected.
                    this.DeleteColumns(args);
                }
            }
        }

        private void dataGridViewSelectedColumns_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            // Ignoring this event while loading columns.
            if (this.isLoading)
            {
                return;
            }

            if ((e.ColumnIndex == this.gridcolumnDistinctOutputColumn.Index || e.ColumnIndex == this.gridcolumnDuplicateOutputColumn.Index)
                && e.RowIndex >= 0)
            {
                if (this.ChangeOutputColumnName != null)
                {
                    ChangeOutputColumnNameArgs args = new ChangeOutputColumnNameArgs();
                    DataGridViewCell currentCell = this.dataGridViewSelectedColumns.Rows[e.RowIndex].Cells[e.ColumnIndex];
                    args.OutputColumn = new DataFlowElement(currentCell.Value.ToString(), currentCell.Tag);

                    this.ChangeOutputColumnName(this, args);
                }
            }
        }

        #endregion
    }

    # region Helper structures and delegates

    internal struct AvailableColumnElement
    {
        public bool Selected;
        public DataFlowElement AvailableColumn;
    }

    internal struct SelectedInputOutputColumns
    {
        public DataFlowElement InputColumn;
        public DataFlowElement DistinctColumn;
        public DataFlowElement DuplicateColumn;
    }

    internal class AvailableColumnsArgs
    {
        public AvailableColumnElement[] AvailableColumns;
    }

    internal delegate void GetAvailableColumnsEventHandler(object sender, AvailableColumnsArgs args);

    internal class SetInputOutputColumnsArgs
    {
        public DataFlowElement VirtualColumn;
        public SelectedInputOutputColumns GeneratedColumns;
        public bool CancelAction;
    }

    internal delegate void ChangeInputOutputColumnsEventHandler(object sender, SetInputOutputColumnsArgs args);

    internal class SelectedInputOutputColumnsArgs
    {
        public SelectedInputOutputColumns[] SelectedColumns;
    }

    internal delegate void GetSelectedInputOutputColumnsEventHandler(object sender, SelectedInputOutputColumnsArgs args);

    internal class ChangeOutputColumnNameArgs
    {
        public DataFlowElement OutputColumn;
        //public bool CancelAction;
    }

    internal delegate void ChangeOutputColumnNameEventHandler(object sender, ChangeOutputColumnNameArgs args);

    #endregion
}